package com.starsky.common.utils.file;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author wansh
 * @version 1.0
 * @des 文件操作类
 * @date 2020/9/19 22:42
 * @email 1057718341@qq.com
 */
@Slf4j
public class FileUtil {

    private static Properties prop = new Properties();

    private static Map<String, String> map = new HashMap<String, String>();
    /**
     * 文件读取缓冲区大小
     */
    private static final int CACHE_SIZE = 1024;

    /**
     * 删除一个文件或者一个文件夹下的所有文件(删除后无法恢复!!!)
     *
     * @param filePath    文件路径
     * @param isDeleteDir 是否删除目录，true-删除，false-不删除
     */
    public static void clearAndDelete(String filePath, boolean isDeleteDir) {
        File file = new File(filePath);
        if (file.exists()) {
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                for (File tempFile : files) {
                    if (tempFile.isDirectory()) {
                        clearAndDelete(tempFile.getAbsolutePath(), isDeleteDir);
                    }
                    tempFile.delete();
                }
            }
            //删除目录
            if (isDeleteDir) {
                file.delete();
            }
        }
    }

    /**
     * 仅删除该目录下的文件,不删除文件夹
     *
     * @param filePath
     */
    public static void deleteFileInForlder(String filePath) {
        File file = new File(filePath);
        File[] files = file.listFiles();
        for (File tempFile : files) {
            if (tempFile.isFile()) {
                tempFile.delete();
            }
        }
    }


    /**
     * 将文件存储到指定目录
     *
     * @param fileStream 文件流
     * @param basePath   文件路径
     * @param fileNo     文件编号
     * @throws Exception
     */
    public static String transferToImage(String fileStream, String basePath, String fileNo) throws Exception {
//        String basePath = request.getRealPath(File.separator);
        String relativePath = "upload/picture/" + System.currentTimeMillis() + "_" + fileNo + ".jpg";
        String imgFilePath = basePath + relativePath;// "D:\\testdata\\222.jpg";//新生成的图片
        File file = new File(basePath + "upload/picture/");
        // 如果文件夹不存在则创建
        if (!file.exists() && !file.isDirectory()) {
            file.mkdir();
        } else {
            clearAndDelete(basePath + "upload/picture/", false);
        }
        boolean t = fileStream.matches("^[A-Za-z0-9]+$");
        try {
            if (!t) {
                generateImageByBASE64(fileStream, imgFilePath);
            } else {
                generateImage(fileStream, imgFilePath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return relativePath;
    }

    /**
     * 将文件存储到指定目录
     *
     * @param stream      文件流
     * @param imgFilePath 文件路径
     * @throws Exception
     */
    public static void generateImage(String stream, String imgFilePath) throws Exception {
        OutputStream out = new FileOutputStream(imgFilePath);
        byte[] fileStream = hex2byte(stream);
        InputStream in = new ByteArrayInputStream(fileStream);
        try {
            byte[] b = new byte[1024];
            int nRead = 0;
            while ((nRead = in.read(b)) != -1) {
                out.write(b, 0, nRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            in.close();
            out.close();
        }
    }

    /**
     * 将文件流解密，并写入到指定目录
     *
     * @param stream      文件流
     * @param imgFilePath 文件目录
     * @throws Exception
     */
    public static void generateImageByBASE64(String stream, String imgFilePath) throws Exception {
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            // Base64解码
            byte[] b = decoder.decodeBuffer(stream);
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {// 调整异常数据
                    b[i] += 256;
                }
            }
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(b);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 字符串转二进制
     *
     * @param str
     * @return
     */
    public static byte[] hex2byte(String str) {
        if (str == null) {
            return null;
        }
        str = str.trim();
        int len = str.length();
        if (len == 0 || len % 2 == 1) {
            return null;
        }
        byte[] b = new byte[len / 2];
        try {
            for (int i = 0; i < str.length(); i += 2) {
                b[i / 2] = (byte) Integer.decode("0X" + str.substring(i, i + 2)).intValue();
            }
            return b;
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 文件下载
     *
     * @param byts     二进制流文件
     * @param fileName 包含文件后缀名即包含类型
     */
    public static void writeToResponse(HttpServletResponse response, byte[] byts, String fileName) {
        // 清空response
        response.reset();
        // 设置response的Header
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        response.addHeader("Content-Length", "" + byts.length);
        response.setContentType("Application/octet-stream;charset=utf-8");
        try {
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(byts);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成导出文件名称
     *
     * @param fileName
     */
    public static String getExportFileName(String fileName) {
        // 设置要导出的文件名称
        StringBuilder sub = new StringBuilder();
        sub.append(fileName).append("-");
        SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = datetimeFormat.format(new Date());
        sub.append(date);
        String str = sub.toString();
        return str;
    }

    /**
     * 将文件保存到指定位置
     *
     * @return String 返回类型
     */
    public static void writeFile(InputStream input, OutputStream output) throws IOException {
        IOUtils.copy(input, output);
        IOUtils.closeQuietly(output);
        IOUtils.closeQuietly(input);
    }

    /**
     * 将文件保存到指定位置
     *
     * @return String 返回类型
     */
    public static void writeFile(Reader input, Writer output) throws IOException {
        IOUtils.copy(input, output);
        IOUtils.closeQuietly(output);
        IOUtils.closeQuietly(input);
    }

    /**
     * 获取文件类型
     *
     * @param fileName
     * @return
     */
    public static String getContentType(String fileName) {
        if (fileName.indexOf(".docx") > -1) {
            return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        } else if (fileName.indexOf(".doc") > -1) {
            return "application/msword";
        } else if (fileName.indexOf(".pdf") > -1) {
            return "application/pdf";
        } else if (fileName.indexOf(".xlsx") > -1) {
            return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        } else if (fileName.indexOf(".xls") > -1) {
            return "application/vnd.ms-excel";
        } else if (fileName.indexOf(".pptx") > -1 || fileName.indexOf(".pptm") > -1) {
            return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
        } else if (fileName.indexOf(".ppt") > -1) {
            return "application/vnd.ms-powerpoint";
        } else if (fileName.indexOf(".jpg") > -1) {
            return "application/x-jpg";
        } else if (fileName.indexOf(".gif") > -1) {
            return "image/gif";
        } else if (fileName.indexOf(".png") > -1) {
            return "application/x-png";
        }
        return null;
    }


    /**
     * 获取真实文件名
     *
     * @param fileName：带路径的文件
     */
    public static String getFileRealName(String fileName) {
        // 真实文件名
        String realName = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.length());
        return realName;
    }

    /**
     * 获取文件后缀
     *
     * @param fileName：文件名
     * @return:文件后缀
     */
    public static String getFileSubfix(String fileName) {
        // 真实文件名
        String fileSubfix = fileName.substring(fileName.lastIndexOf("."), fileName.length());
        return fileSubfix;
    }

    /**
     * 下载文件
     *
     * @param filePath 带文件命令的路径
     * @param buf      文件内容
     * @throws Exception
     */
    public static void donwload(String filePath, byte[] buf) throws Exception {
        if (buf != null && !"".equals(buf)) {
            OutputStream out = new FileOutputStream(new File(filePath));
            out.write(buf, 0, buf.length);
            out.flush();
            out.close();
        }
    }

    /**
     * 下载文件并返回http访问链接
     *
     * @param request  请求头信息
     * @param filePath 带路径的文件名
     * @param buf      文件内存
     * @return
     */
    public static String download(HttpServletRequest request, String filePath, byte[] buf) {
        String path = "";
        try {
            /* 下载图片至本地  */
            // 项目真实路径
            String file = request.getSession().getServletContext().getRealPath(File.separator);

            // 下载图片
            FileUtil.donwload(file + filePath, buf);
            // 项目路径
            String projectName = request.getContextPath();
            // http访问路径
            path = projectName + filePath;
            /* 下载图片至本地  */
        } catch (Exception e) {
            e.printStackTrace();
        }
        return path;
    }


    /**
     * 获取文件属性
     *
     * @param filePaths
     * @return
     */
    public static Properties loadFiles(String... filePaths) {
        for (int i = 0, j = filePaths.length; i < j; i++) {
            InputStream input = null;
            try {
                input = FileUtil.class.getClassLoader().getResourceAsStream(filePaths[i]);
                prop.load(input);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                IOUtils.closeQuietly(input);
            }
        }
        return prop;
    }


    /**
     * 加载指定的properties配置文件到全局的map对象中
     *
     * @param filePath
     */
    private static Map<String, String> loadFile(String filePath) {
        // 判断map是否为空
        if (null == map) {
            // 当map为空时初始化map对象
            map = new HashMap<String, String>();
        }
        if (null == filePath || "".equals(filePath.trim())) {
            System.out.println("The file path is null,return");
            return null;
        }
        filePath = filePath.trim();
        // 获取资源文件
        InputStream is = FileUtil.class.getClassLoader().getResourceAsStream(filePath);
        // 属性列表
        Properties prop = new Properties();
        try {
            // 从输入流中读取属性列表
            prop.load(is);
            // 返回Properties中包含的key-value的Set视图
            Set<Map.Entry<Object, Object>> set = prop.entrySet();
            // 返回在此Set中的元素上进行迭代的迭代器
            Iterator<Map.Entry<Object, Object>> it = set.iterator();
            // 循环取出key-value
            while (it.hasNext()) {
                Map.Entry<Object, Object> entry = it.next();
                // 获取properties的key
                String key = entry.getKey().toString();
                // 获取properties的value
                String value = entry.getValue() == null ? "" : entry.getValue().toString();
                // 把遍历的properties的数据添加到map对象中
                map.put(key, value);
            }
        } catch (IOException e) {
            System.out.println("load file faile." + e);
        }
        return map;
    }

    /**
     * 根据传递的key获取map中的值
     *
     * @param key 文件中的key
     * @return key对应的结果
     */
    public static String getValue(String key) {
        if (null == map) {
            System.out.println("map is null!");
            return null;
        }
        return map.get(key);
    }

    /**
     * 给对象的所有属性设置默认值
     */
    public static void setDefaultValForObj(Object obj) {
        try {
            // 获取实体类的所有属性并赋上默认值
            Class<?> clazz = obj.getClass();
            for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环  遍历父类
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    if (field.get(obj) != null) {
                        continue;
                    }
                    String type = field.getGenericType().toString();
                    // 如果type是类类型，则前面包含"class "，后面跟类名
                    if (type.equals("class java.lang.String")) {
                        field.set(obj, "");
                    } else if (type.equals("class java.lang.Integer")) {
                        field.set(obj, 0);
                    } else if (type.equals("class java.lang.Long")) {
                        field.set(obj, 0L);
                    } else if (type.equals("class java.math.BigDecimal")) {
                        field.set(obj, new BigDecimal(0));
                    } else if (type.equals("class java.lang.Double")) {
                        field.set(obj, 0);
                    } else if (type.equals("class java.lang.Boolean")) {
                        field.set(obj, false);
                    } else if (type.equals("class java.util.Date")) {
                        field.set(obj, new Date());
                    }

                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 拷贝源对象的属性的值到目标对象的同名属性
     */
    public static void setSourceValToTargetVal(Object source, Object target) {
        try {
            Field[] fieldSources = source.getClass().getDeclaredFields();
            for (Field field : fieldSources) {
                field.setAccessible(true);
                if (field.get(source) == null) {
                    continue;
                }

                Field f = target.getClass().getDeclaredField(field.getName());
                f.setAccessible(true);
                f.set(target, field.get(source));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将传入的字符串转小写后在将首字母大写
     * 效率是最高的
     */
    private static String convertToCamel(String fildeName) {
        byte[] items = fildeName.toLowerCase().getBytes();
        items[0] = (byte) ((char) items[0] - 'a' + 'A');
        return new String(items);
    }

    /**
     * 获取根目录
     */
    public static String getRootPath() {
        //获取根目录
        String rootPath = System.getProperty("java.class.path");
        // rootPath = D:\aaaaaaaaa\climb-fts-webapi-0.0.1-SNAPSHOT-exec.jar
        int firstIndex = rootPath.lastIndexOf(System.getProperty("path.separator")) + 1;
        // firstIndex = 0
        int lastIndex = rootPath.lastIndexOf(File.separator) + 1;
        // lastIndex = 13
        rootPath = rootPath.substring(firstIndex, lastIndex);
        // rootPath = D:\aaaaaaaaa\
        return rootPath;
    }

    /**
     * 根据系统 返回RootPath+dirName的路径
     */
    public static String getPathByRoot(String dirName) {

        String rootPath = "";
        String path = getRootPath();
        if ("\\".equals(File.separator)) {
            // windows path
            rootPath = path.replace("/", "\\") + dirName + "\\";
        } else if ("/".equals(File.separator)) {
            // linux path
            rootPath = path.replace("\\", "/") + dirName + "/";
        }
        if (!createDir(rootPath)) {
            System.out.println("目录创建失败");
        }
        return rootPath;
    }

    /**
     * 创建目录
     */
    public static boolean createDir(String destDirName) {

        File dir = new File(destDirName);
        // 判断目录是否存在
        if (dir.exists()) {
            return true;
        }
        // 创建目标目录
        if (dir.mkdirs()) {
            return true;
        }
        System.out.println("目录创建失败：" + destDirName);
        return false;
    }

    /**
     * 删除文件及文件夹
     */
    public static void deleteFileOrDir(String filePath) {

        try {

            File file = new File(filePath);
            if (!file.exists()) {
                return;
            }

            FileUtils.forceDelete(file);
        } catch (Exception e) {
            log.error("异常[{}]", e.toString());
        }

    }

    /**
     * 异步删除文件及文件夹
     */
    public static void deleteFileOrDirAsync(String filePath) {
        deleteFileOrDir(filePath);
    }

    /**
     * 文件转64位编码
     */
    public static String getFileToCode(MultipartFile file) {
        InputStream in;
        byte[] data = null;

        try {
            in = file.getInputStream();
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException var4) {
            var4.printStackTrace();
        }
        return new String(org.apache.commons.codec.binary.Base64.encodeBase64(data));
    }


    public static String getKey(String splitChar, String... str) {
        String rstStr = "";
        for (String temp : str) {
            rstStr += temp + splitChar;
        }
        return rstStr.substring(0, rstStr.length() - splitChar.length());
    }

    // 读取流信息并返回
    public static String readInputStream(InputStream inputStream) throws IOException {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line).append("\n");
            }
        }
        return sb.toString();
    }


    /**
     * <p>
     * BASE64字符串解码为二进制数据
     * </p>
     *
     * @param base64
     * @return
     * @throws Exception
     */
    public static byte[] decode(String base64) throws Exception {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] buf = decoder.decodeBuffer(base64);
        return buf;
    }

    /**
     * <p>
     * 二进制数据编码为BASE64字符串
     * </p>
     *
     * @param bytes
     * @return
     * @throws Exception
     */
    public static String encode(byte[] bytes) throws Exception {
        BASE64Encoder base64 = new BASE64Encoder();
        return new String(base64.encode(bytes));
    }

    /**
     * <p>
     * 将文件编码为BASE64字符串
     * </p>
     * <p>
     * 大文件慎用，可能会导致内存溢出
     * </p>
     *
     * @param filePath 文件绝对路径
     * @return
     * @throws Exception
     */
    public static String encodeFile(String filePath) throws Exception {
        byte[] bytes = fileToByte(filePath);
        return encode(bytes);
    }

    /**
     * <p>
     * BASE64字符串转回文件
     * </p>
     *
     * @param filePath 文件绝对路径
     * @param base64   编码字符串
     * @throws Exception
     */
    public static void decodeToFile(String filePath, String base64) throws Exception {
        byte[] bytes = decode(base64);
        byteArrayToFile(bytes, filePath);
    }

    /**
     * <p>
     * 文件转换为二进制数组
     * </p>
     *
     * @param filePath 文件路径
     * @return
     * @throws Exception
     */
    public static byte[] fileToByte(String filePath) throws Exception {
        byte[] data = new byte[0];
        File file = new File(filePath);
        if (file.exists()) {
            FileInputStream in = new FileInputStream(file);
            ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
            byte[] cache = new byte[CACHE_SIZE];
            int nRead = 0;
            while ((nRead = in.read(cache)) != -1) {
                out.write(cache, 0, nRead);
                out.flush();
            }
            out.close();
            in.close();
            data = out.toByteArray();
        }
        return data;
    }

    /**
     * <p>
     * 二进制数据写文件
     * </p>
     *
     * @param bytes    二进制数据
     * @param filePath 文件生成目录
     */
    public static void byteArrayToFile(byte[] bytes, String filePath) throws Exception {
        InputStream in = new ByteArrayInputStream(bytes);
        File destFile = new File(filePath);
        if (!destFile.getParentFile().exists()) {
            destFile.getParentFile().mkdirs();
        }
        destFile.createNewFile();
        OutputStream out = new FileOutputStream(destFile);
        byte[] cache = new byte[CACHE_SIZE];
        int nRead = 0;
        while ((nRead = in.read(cache)) != -1) {
            out.write(cache, 0, nRead);
            out.flush();
        }
        out.close();
        in.close();
    }

    // 将 s 进行 BASE64 编码
    public static String getBASE64(String s) {
        if (s == null)
            return null;
        try {
            String str = (new BASE64Encoder()).encode(s.getBytes("UTF-8"));
            str = str.replaceAll("\\+", "-");
            str = str.replaceAll("\\/", "*");
            return str;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 将 BASE64 编码的字符串 s 进行解码
    public static String getFromBASE64(String s) {
        if (s == null)
            return null;
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            s = s.replaceAll("-", "+");
            s = s.replaceAll("\\*", "/");
            byte[] b = decoder.decodeBuffer(s);
            return new String(b, "UTF-8");
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * zip文件下载
     *
     * @param response
     * @return
     */
    public static void downLoadZip(HttpServletResponse response, String fileName, String zipFile) {

        try {
            File file = new File(zipFile);
            response.setCharacterEncoding("UTF-8");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes("ISO8859-1"), "UTF-8"));
            response.setContentLength((int) file.length());
            response.setContentType("application/zip");// 定义输出类型

            BufferedInputStream buff = new BufferedInputStream(new FileInputStream(file));
            OutputStream myout = response.getOutputStream();// 从response对象中得到输出流,准备下载

            byte[] b = new byte[1024];// 相当于我们的缓存
            long k = 0;// 该值用于计算当前实际下载了多少字节
            // 开始循环下载
            while (k < file.length()) {
                int j = buff.read(b, 0, 1024);
                k += j;
                myout.write(b, 0, j);
            }
            buff.close();
            myout.flush();
            myout.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }


    /**
     * 通过文件路径下载到本地
     * 例如：https://uni-translate.oss-cn-beijing.aliyuncs.com/images/1649380052839123.png
     *
     * @param fileUrl：文件路径，
     * @param destFile：     目标存储文件
     */
    public static void downLoad(String fileUrl, String destFile) {
        try {
            URL httpurl = new URL(fileUrl);
            File file = new File(destFile);
            FileUtils.copyURLToFile(httpurl, file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {

        String value = getValue("timeCount");
        System.out.println(value);


        Properties pro = loadFiles("data-sync.properties");
        String val = pro.getProperty(null);
        System.out.println(val);

        String fileName = "\\app\\file.txt";
        String realName = fileName.substring(fileName.lastIndexOf(File.separator) + 1, fileName.length());
        System.out.println(realName);


        String str = "省市县发文流程";
        String base64 = getBASE64(str);
        System.out.println(base64);
    }
}
